package com.bwton.msx.ccb.service.api;

import android.util.Log;

import com.bwton.msx.ccb.service.api.adapter.RxJava2CallAdapterFactory;
import com.bwton.msx.ccb.service.api.annotations.EncryptParams;
import com.bwton.msx.ccb.service.api.annotations.TokenRequired;
import com.bwton.msx.ccb.service.api.response.ApiResponse;
import com.bwton.msx.ccb.service.api.response.IgnoreResult;
import com.bwton.msx.ccb.service.api.utils.AESUtil;
import com.bwton.msx.ccb.service.api.utils.AnnotationUtils;
import com.bwton.msx.ccb.service.api.utils.RSA2Utils;
import com.bwton.msx.ccb.service.api.utils.SequenceUtil;
import com.bwton.msx.ccb.service.user.UserManager;
import com.bwton.msx.ccb.utils.Configuration;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;

import org.apache.redefine.digest.DigestUtils;
import org.json.JSONObject;

import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import okhttp3.Call;
import okhttp3.HttpUrl;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okhttp3.logging.HttpLoggingInterceptor;
import okio.Buffer;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

/**
 * 管理所有apiservice
 */
public class ApiServiceManager {
    private static AuthApiService authApiService;
    private static Gson gson = new Gson();
    public static AuthApiService getAuth() {
        if (authApiService == null) {
            synchronized (AuthApiService.class) {
                if (authApiService == null) {
                    HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(message ->
                            Log.d("OkHttp", message));
                    interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

                    AnnotationUtils.loadApiService(AuthApiService.class);
                    OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
                    OkHttpClient client = clientBuilder.callTimeout(Configuration.HTTP_TIMEOUT, TimeUnit.MILLISECONDS)
                            .readTimeout(Configuration.HTTP_TIMEOUT, TimeUnit.MILLISECONDS)
                            .writeTimeout(Configuration.HTTP_TIMEOUT, TimeUnit.MILLISECONDS)
                            .addNetworkInterceptor(interceptor)
                            .addInterceptor(new Interceptor() {
                                @Override
                                public Response intercept(Chain chain) {
                                    try {
                                        Response response = interceptAuthRequest(chain);
                                        if (response.code() == 502){
                                            Response.Builder builder = response.newBuilder();
                                            ApiResponse<IgnoreResult> res = new ApiResponse<>();
                                            res.success = false;
                                            res.errcode = "502";
                                            res.errmsg = "服务器错误:502 Bad Gateway";
                                            builder.body(ResponseBody.create(gson.toJson(res), MediaType.get("application/json; charset=UTF-8")));
                                            builder.code(200);
                                            return builder.build();
                                        }
                                        return response;
                                    } catch (Exception e) {
                                        e.printStackTrace();
                                        return null;
                                    }
                                }
                            })
                            .build();
                    Retrofit.Builder builder = new Retrofit.Builder();
                    builder.baseUrl(Configuration.URL_AUTH)
                            .client(client)
                            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                            .addConverterFactory(GsonConverterFactory.create());
                    authApiService = builder.build().create(AuthApiService.class);
                }
            }
        }
        return authApiService;
    }

    private static Response interceptAuthRequest(Interceptor.Chain chain) throws Exception {
        Request request = chain.request();
        String path = request.url().encodedPath();
        //获取原始的请求body数据
        Request.Builder builder = request.newBuilder();
        Buffer buffer = new Buffer();
        request.body().writeTo(buffer);
        String jsonString = buffer.readUtf8();
        buffer.close();

        //token判定是否添加
        if(AnnotationUtils.tokenRequired(path)){
            //需要token等用户信息
            UserManager userManager = UserManager.getInstance();
            builder.header("token",userManager.token());
            builder.header("unionId",userManager.unionId());
        }
        //添加安全验证相关参数
        String nonce = SequenceUtil.generateRandomString(20);
        String timestamp = String.valueOf(new Date().getTime());
        String appid = Configuration.APP_ID;
        String merchantId = Configuration.MERCHANT_ID;
        String placeCode = Configuration.PLACE_CODE;

        String VI = SequenceUtil.generateRandomString(16);
        String KEY = SequenceUtil.generateRandomString(16);
        String random = "AES/CBC:" + VI + "," + KEY;
        String encryptedRandom = RSA2Utils.encryptByPublicKey(random, Configuration.API_PUBLIC_KEY);
        String sequence = SequenceUtil.genSequence();

        String[] encryptParams =AnnotationUtils.encryptParams(path);
        if (encryptParams.length != 0){
            Map<String,String> params = gson.fromJson(jsonString,new TypeToken<Map<String,String>>(){}.getType());
            for (String key : encryptParams) {
                if(!params.containsKey(key)){
                    continue;
                }
                String value = params.get(key);
                if (value == null){
                    continue;
                }
                String replaceValue = AESUtil.encryptToHex(value,random);
                params.put(key,replaceValue);
            }
            jsonString = gson.toJson(params);
        }
        builder.post(RequestBody.create(request.body().contentType(),jsonString));
        String unSignedString = "appid=" +
                appid +
                "&message=" +
                jsonString +
                "&nonce=" +
                nonce +
                "&random=" +
                encryptedRandom +
                "&sequence=" + sequence +
                "&timestamp=" + timestamp +
                "&version=" + Configuration.API_VERSION;
        String digest = DigestUtils.sha256Hex(unSignedString.getBytes(StandardCharsets.UTF_8));
        String signature = RSA2Utils.encryptByPublicKey(digest, Configuration.API_PUBLIC_KEY);
        builder.header("version", Configuration.API_VERSION)
                .header("appid", appid)
                .header("cityId", Configuration.CITY_ID)
                .header("placeCode", placeCode)
                .header("sequence", sequence)
                .header("timestamp", timestamp)
                .header("random", encryptedRandom)
                .header("nonce", nonce)
                .header("signature", signature)
                .header("merchantId", merchantId);
        Request realRequest = builder.build();
        Response response =  chain.proceed(realRequest);
        Response.Builder resBuilder = response.newBuilder();
        String body = response.body().string();
        JSONObject jsonObject = new JSONObject(body);
        jsonObject.put("sequence",sequence);
        resBuilder.body(ResponseBody.create(jsonObject.toString(),MediaType.get("application/json; charset=UTF-8")));
        return resBuilder.build();
    }
}
